package com.pic.mgt.syushin.action;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;

import com.pic.mgt.dao.syushin.ISyushinBusinessApDao;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.manager.business.ApInfoBean;
import com.pic.mgt.manager.business.BasicBusinessInfoBean;
import com.pic.mgt.monitor.UnitMonitor;
import com.pic.mgt.monitor.UnitMonitorException;
import com.pic.mgt.syushin.manager.business.SyushinBusinessManager;
import com.pic.mgt.utils.IgxCollectionUtils;
import com.pic.mgt.utils.IgxFileUtils;

// (送多方新增)
public class SyushinBusinessApMgtActionImpl implements
		SyushinBusinessApMgtAction {
	
	private ISyushinBusinessApDao syushinBusinessApDao;
	private SyushinBusinessManager businessManager;
	private static String syushinOutboxBucketPathStr;
	private static String syushinOutboxRealPathStr;
	private UnitMonitor messageUnitMonitor;
	private String REAL_SYUSHIN;
	private String BATCH_SYUSHIN;
	
	@Override
	public synchronized boolean addSyushinBusinessAp(String businessName, String apName) {
		if (null == businessName || (businessName = businessName.trim()).isEmpty()) {
			// ********************** Log **********************
			IgxLog.messageLog.error(this,
					"Adding syushin business AP system failed. Do not provide business name.");
			// ********************** Log **********************
			return false;
		}
		if (null == apName || (apName = apName.trim()).isEmpty()) {
			// ********************** Log **********************
			IgxLog.messageLog.error(this,
					"Adding syushin business AP system failed. Do not provide AP name.");
			// ********************** Log **********************
			return false;
		}
		
		synchronized(this.businessManager.getBasicBusinessInfoBeanMap()) { // 與業務同步進行同步
			// 判斷是否有該業務
			boolean isEffectiveBusinessName = this.businessManager.isEffectiveBusiness(businessName);
			if (isEffectiveBusinessName) {
				BasicBusinessInfoBean basicBusinessInfoBean = this.businessManager.getBasicBusinessInfoBean(businessName);
				synchronized (basicBusinessInfoBean.getSyushinBusinessApList()) {
					try {
						// 新增對應的目錄
						File syushinOutboxForAp = createSyushinOutboxApFolder(basicBusinessInfoBean, apName);
						// 將 AP Name 新增到DB中
						this.syushinBusinessApDao.insertBasic(
								IgxCollectionUtils.convertToMap(
										ISyushinBusinessApDao._businessName, businessName,
										ISyushinBusinessApDao._apName, apName));
						// 將 AP Name 放到業務AP List清單中中
						addApSystem(basicBusinessInfoBean, apName);
						// 將此目錄加入監控（用以監控訊息檔)
						this.messageUnitMonitor.addMonitorPath(syushinOutboxForAp.getAbsolutePath());
						
						// ********************** Log **********************
						IgxLog.messageLog.info(this, "Adding AP system '{}' to business '{}' completed.", apName, businessName);
						// ********************** Log **********************
						
						return true;
					} catch (SecurityException e) {
						// ********************** Log **********************
						IgxLog.messageDetailsLog.error(this, e.getMessage());
						// ********************** Log **********************
						
						// ********************** Log **********************
						IgxLog.messageLog.error(this,
										"SUDA-340 Adding ap system '{}' to business '{}' occured a folder creation error.",
										apName, businessName);
						// ********************** Log **********************
						return false;
					} catch (DuplicateKeyException e) {
						// 代表該業務已經存在有該AP系統了
						
						// 將 AP Name 放到業務清單中
						addApSystem(basicBusinessInfoBean, apName);
						
						// ********************** Log **********************
						IgxLog.messageLog.warn(this,
								"The AP system '{}' has already existed in the business '{}'.", apName, businessName);
						// ********************** Log **********************
						return true;
					} catch (DataAccessException e) {
						// ********************** Log **********************
						IgxLog.messageDetailsLog.error(this, e.getMessage());
						// ********************** Log **********************
						
						// ********************** Log **********************
						IgxLog.messageLog.error(this,
										"SUDA-338 Adding ap system '{}' to business '{}' occured a database operation error.",
										apName, businessName);
						// ********************** Log **********************
						return false;
					} catch (UnitMonitorException e) {
						// ********************** Log **********************
						IgxLog.messageLog.error(this,
										"SUDA-344 Adding ap system '{}' to business '{}' occured a folder monitor error.",
										apName, businessName);
						// ********************** Log **********************
						return false;
					}
				}
			}
			else {
				// ********************** Log **********************
				IgxLog.messageLog.error(this,
						"Adding syushin business AP system failed. Business name '{}' is ineffective.", businessName);
				// ********************** Log **********************
				return false;
			}
		}
	}
	
	/**
	 * 在 Syushin Outbox 底下建立出目錄,名稱即為系統名稱。會根據其型態，如 Real 即會建在 Real 集信 Outbox 目錄底下；Bucket 即會建在 Bucket 集信 Outbox 目錄底下。
	 * @param basicBusinessInfoBean
	 * @param apName
	 * @return 已建立出的目錄
	 */
	private File createSyushinOutboxApFolder(BasicBusinessInfoBean basicBusinessInfoBean, String apName) throws SecurityException {
		File syushinOutboxForAp = null;
		if (basicBusinessInfoBean.getBusinessType().equals(this.REAL_SYUSHIN)) {
			syushinOutboxForAp = new File(SyushinBusinessApMgtActionImpl.syushinOutboxRealPathStr, apName);
		}
		else if (basicBusinessInfoBean.getBusinessType().equals(this.BATCH_SYUSHIN)) {
			syushinOutboxForAp = new File(SyushinBusinessApMgtActionImpl.syushinOutboxBucketPathStr, apName);
		}
		
		if (null != syushinOutboxForAp) {
			syushinOutboxForAp.mkdirs();
		}
		
		return syushinOutboxForAp;
	}
	
	/**
	 * 判斷 apName是否已存在 List中,如果已存在則不會放入list;反之則會將apName加入List
	 * @param list
	 * @param apName
	 */
	private void addApSystem(BasicBusinessInfoBean basicBusinessInfoBean, String apName) {
		List<ApInfoBean> apInfoBeanList = basicBusinessInfoBean.getSyushinBusinessApList();
		
		boolean isApNameContainInList = false;
		for (ApInfoBean apInfoBean : apInfoBeanList) {
			if (apInfoBean.getApName().equals(apName)) {
				isApNameContainInList = true;
				break;
			}
		}
		
		if (!isApNameContainInList) {
			ApInfoBean newApInfoBean = new ApInfoBean(apName);
			apInfoBeanList.add(newApInfoBean);
			fillTheApFolderIntoBasicBusinessInfoBean(basicBusinessInfoBean);
		}
	}
	
	@Override
	public synchronized boolean deleteSyushinBusinessAp(String businessName, String apName) {
		if (null == businessName || (businessName = businessName.trim()).isEmpty()) {
			// ********************** Log **********************
			IgxLog.messageLog.error(this,
					"Deleting syushin business AP system failed. Do not provide business name.");
			// ********************** Log **********************
			return false;
		}
		if (null == apName || (apName = apName.trim()).isEmpty()) {
			// ********************** Log **********************
			IgxLog.messageLog.error(this,
					"Deleting syushin business AP system failed. Do not provide AP name.");
			// ********************** Log **********************
			return false;
		}
		
		synchronized(this.businessManager.getBasicBusinessInfoBeanMap()) { // 與業務同步進行同步
			// 判斷是否有該業務
			boolean isEffectiveBusinessName = this.businessManager.isEffectiveBusiness(businessName);
			if (isEffectiveBusinessName) {
				BasicBusinessInfoBean basicBusinessInfoBean = this.businessManager.getBasicBusinessInfoBean(businessName);
				
				if (!isSyushinBusinessApListContainAp(basicBusinessInfoBean.getSyushinBusinessApList(), apName)) {
					// ********************** Log **********************
					IgxLog.messageLog.error(this,
							"Deleting syushin business AP system failed. '{}' is not existed in business '{}'.",
							apName, businessName);
					// ********************** Log **********************
					return false;
				}
				
				synchronized(basicBusinessInfoBean.getSyushinBusinessApList()) {
					try {
						// 刪除資料庫
						this.syushinBusinessApDao.deleteByBizNameAndApName(businessName, apName);
						// 將 AP Name 從業務清單中移除
						removeApFromSyushinBusinessApList(basicBusinessInfoBean.getSyushinBusinessApList(), apName);
						// 刪除對應的目錄(如果有必要)
						deleteSyushinOutboxApFolder(basicBusinessInfoBean, apName);
						
						// ********************** Log **********************
						IgxLog.messageLog.info(this, "Removing AP system '{}' from business '{}' completed.", apName, businessName);
						// ********************** Log **********************
						
						return true;
					} catch (DataAccessException e) {
						// ********************** Log **********************
						IgxLog.messageDetailsLog.error(this, e.getMessage());
						// ********************** Log **********************
						
						// ********************** Log **********************
						IgxLog.messageLog.error(this,
										"SUDA-339 Removing ap system '{}' from business '{}' occured a database operation error.",
										apName, businessName);
						// ********************** Log **********************
						return false;
					} catch (IOException e) {
						// ********************** Log **********************
						IgxLog.messageDetailsLog.error(this, e.getMessage());
						// ********************** Log **********************
						
						// ********************** Log **********************
						IgxLog.messageLog.error(this,
										"SUDA-341 Removing ap system '{}' from business '{}' occured a folder deletion error.",
										apName, businessName);
						// ********************** Log **********************
						return false;
					}
				}
			}
			else {
				// ********************** Log **********************
				IgxLog.messageLog.error(this,
						"Deleting syushin business AP system failed. Business name '{}' is ineffective.", businessName);
				// ********************** Log **********************
				return false;
			}
		}
	}
	
	private boolean isSyushinBusinessApListContainAp(List<ApInfoBean> apInfoBeanList, String apName) {
		for (ApInfoBean apInfoBean : apInfoBeanList) {
			if (apInfoBean.getApName().equals(apName)) {
				return true;
			}
		}
		return false;
	}
	
	private void removeApFromSyushinBusinessApList(List<ApInfoBean> apInfoBeanList, String apName) {
		int toBeDeletedIndex = -1;
		
		for (int i=0;i<apInfoBeanList.size();i++) {
			ApInfoBean apInfoBean = apInfoBeanList.get(i);
			if (apInfoBean.getApName().equals(apName)) {
				toBeDeletedIndex = i;
				break;
			}
		}
		
		if (toBeDeletedIndex >= 0) {
			apInfoBeanList.remove(toBeDeletedIndex);
		}
	}
	
	/**
	 * Check 該 AP system 是否仍有被其它業務所使用，如果有就不會將 AP 的目錄刪除；反之則會刪除。
	 * @param basicBusinessInfoBean
	 * @param apName
	 * @throws IOException
	 */
	private void deleteSyushinOutboxApFolder(BasicBusinessInfoBean basicBusinessInfoBean, String apName) throws IOException {
		List<BasicBusinessInfoBean> effBasicBusinessInfoBeanList = this.businessManager.getEffectiveBasicBusinessInfoBeanList();
		
		boolean isTheApStillUse = false;
		for (BasicBusinessInfoBean effBasicBusinessInfoBean : effBasicBusinessInfoBeanList) {
			if (effBasicBusinessInfoBean.getBusinessType().equals(basicBusinessInfoBean.getBusinessType())) { // 相同的業務型態才作比較
				if (effBasicBusinessInfoBean != basicBusinessInfoBean) { // 不要比較到自己
					if (isSyushinBusinessApListContainAp(effBasicBusinessInfoBean.getSyushinBusinessApList(), apName)) {
						isTheApStillUse = true;
						break;
					}
				}
			}
		}
		
		if (!isTheApStillUse) {
			String businessType = basicBusinessInfoBean.getBusinessType();
			File syushinOutboxForAp = null;
			if (businessType.equals(this.REAL_SYUSHIN)) {
				syushinOutboxForAp = new File(SyushinBusinessApMgtActionImpl.syushinOutboxRealPathStr, apName);
			}
			else if (businessType.equals(this.BATCH_SYUSHIN)) {
				syushinOutboxForAp = new File(SyushinBusinessApMgtActionImpl.syushinOutboxBucketPathStr, apName);
			}
			
			if (null != syushinOutboxForAp) {
				// 移除目錄的監控
				this.messageUnitMonitor.removeMonitorPath(syushinOutboxForAp.getAbsolutePath());
				
				// 刪除目錄
				// throw IOException
				FileUtils.deleteDirectory(syushinOutboxForAp);
			}
		}
	}
	
	@Override
	public synchronized String getApNameByBizName(String businessName) {
		if (null == businessName || (businessName = businessName.trim()).isEmpty()) {
			// ********************** Log **********************
			IgxLog.messageLog.error(this,
					"Querying syushin business AP system failed. Do not provide business name.");
			// ********************** Log **********************
			return null;
		}
		
		boolean isEffectiveBusinessName = this.businessManager.isEffectiveBusiness(businessName);
		if (isEffectiveBusinessName) {
			BasicBusinessInfoBean basicBusinessInfoBean = this.businessManager.getBasicBusinessInfoBean(businessName);
			return getApNamesByBasicBusinessInfo(basicBusinessInfoBean);
		}
		else {
			// ********************** Log **********************
			IgxLog.messageLog.error(this,
					"Querying syushin business AP system failed. Business name '{}' is ineffective.", businessName);
			// ********************** Log **********************
			return null;
		}
	}
	
	public String getApNamesByBasicBusinessInfo(BasicBusinessInfoBean basicBusinessInfoBean) {
		List<ApInfoBean> apNameList = basicBusinessInfoBean.getSyushinBusinessApList();
		
		StringBuffer apInfoStringBuf = new StringBuffer();
		
		apInfoStringBuf.append("\r\n");
		apInfoStringBuf.append("Business Name: " + basicBusinessInfoBean.getBusinessName() + "   ");
		apInfoStringBuf.append("Business Type: " + basicBusinessInfoBean.getBusinessType());
		apInfoStringBuf.append("\r\n");
		apInfoStringBuf.append("---------------------------------------------");
		apInfoStringBuf.append("\r\n");
		
		for (ApInfoBean apInfoBean : apNameList) {
			apInfoStringBuf.append("  --> " + apInfoBean.getApName());
			apInfoStringBuf.append("\r\n");
		}
		
		return apInfoStringBuf.toString();
	}
	
	@Override
	public synchronized boolean cleanRedundantApFolder() {
		boolean isCleanSuccess = true;
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Start cleaning redundant ap folder.");
		// ********************** Log **********************
		
		// 清除Real集信Outbox底下多餘的 AP 目錄
		List<File> realSyushinApFolderList = IgxFileUtils.listDirectory(new File(SyushinBusinessApMgtActionImpl.syushinOutboxRealPathStr));
		for (File realSyushinApFolder : realSyushinApFolderList) {
			if (!checkIsApInUse(realSyushinApFolder.getName(), this.REAL_SYUSHIN)) {
				try {
					FileUtils.deleteDirectory(realSyushinApFolder);
					// ********************** Log **********************
					IgxLog.messageLog.info(this,
							"Deleted redundant ap folder '{}'.", realSyushinApFolder.getAbsolutePath());
					// ********************** Log **********************
				} catch (IOException e) {
					isCleanSuccess = false;
					
					// ********************** Log **********************
					IgxLog.messageDetailsLog.error(this, e.getMessage());
					// ********************** Log **********************
					
					// ********************** Log **********************
					IgxLog.messageLog.error(this,
							"SUDA-343 Deleting redundant ap folder '{}' occured an folder operation error.", realSyushinApFolder.getAbsolutePath());
					// ********************** Log **********************
				}
			}
		}
		
		// 清除batch集信Outbox底下多餘的 AP 目錄
		List<File> batchSyushinApFolderList = IgxFileUtils.listDirectory(new File(SyushinBusinessApMgtActionImpl.syushinOutboxBucketPathStr));
		for (File batchSyushinApFolder : batchSyushinApFolderList) {
			if (!checkIsApInUse(batchSyushinApFolder.getName(), this.BATCH_SYUSHIN)) {
				try {
					FileUtils.deleteDirectory(batchSyushinApFolder);
					// ********************** Log **********************
					IgxLog.messageLog.info(this,
							"Deleted redundant ap folder '{}'.", batchSyushinApFolder.getAbsolutePath());
					// ********************** Log **********************
				} catch (IOException e) {
					isCleanSuccess = false;
					
					// ********************** Log **********************
					IgxLog.messageDetailsLog.error(this, e.getMessage());
					// ********************** Log **********************
					
					// ********************** Log **********************
					IgxLog.messageLog.error(this,
							"SUDA-343 Deleting redundant ap folder '{}' occured an folder operation error.", batchSyushinApFolder.getAbsolutePath());
					// ********************** Log **********************
				}
			}
		}
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Cleaning redundant ap folder completed.");
		// ********************** Log **********************
		
		return isCleanSuccess;
	}
	
	/**
	 * 如果 AP Name 有被使用中,則會回傳 true.
	 * @param apName
	 * @return
	 */
	private boolean checkIsApInUse(String apName, String businessType) {
		List<BasicBusinessInfoBean> basicBusinessInfoBeanList = this.businessManager.getEffectiveBasicBusinessInfoBeanList();
		for (BasicBusinessInfoBean basicBusinessInfoBean : basicBusinessInfoBeanList) {
			if (basicBusinessInfoBean.getBusinessType().equals(businessType)) {
				List<ApInfoBean> inUseApInfoBeanList = basicBusinessInfoBean.getSyushinBusinessApList();
				for (ApInfoBean inUseApInfoBean : inUseApInfoBeanList) {
					if (inUseApInfoBean.getApName().equals(apName)) {
						return true;
					}
				}
			}
		}
		return false;
	}

	@Override
	public boolean cleanRedundantApInDB() {
		try {
			this.syushinBusinessApDao.deleteIneffectiveBusinessNameData();
			
			// ********************** Log **********************
			IgxLog.messageLog.info(this,
					"Cleaning redundant AP data in database completed.");
			// ********************** Log **********************
			
			return true;
		} catch (DataAccessException e) {
			// ********************** Log **********************
			IgxLog.messageDetailsLog.error(this, e.getMessage());
			// ********************** Log **********************
			
			// ********************** Log **********************
			IgxLog.messageLog.error(this,
							"SUDA-342 Cleaning redundant AP data in database occured a database operation error.");
			// ********************** Log **********************
			return false;
		}
	}
	
	@Override
	public synchronized List<File> getAllApFolders() {
		Map<String ,ApInfoBean> realApNamesMap = new HashMap<String, ApInfoBean>();
		Map<String ,ApInfoBean> bucketApNamesMap = new HashMap<String, ApInfoBean>();
		
		List<BasicBusinessInfoBean> effBasicBusinessInfoBeanList = this.businessManager.getEffectiveBasicBusinessInfoBeanList();
		for (BasicBusinessInfoBean effBasicBusinessInfoBean : effBasicBusinessInfoBeanList) {
			if (effBasicBusinessInfoBean.getBusinessType().equals(this.REAL_SYUSHIN)) {
				putApNameToMap(effBasicBusinessInfoBean.getSyushinBusinessApList(), realApNamesMap);
			}
			else if (effBasicBusinessInfoBean.getBusinessType().equals(this.BATCH_SYUSHIN)) {
				putApNameToMap(effBasicBusinessInfoBean.getSyushinBusinessApList(), bucketApNamesMap);
			}
		}
		
		List<File> apFoldersList = new LinkedList<File>();
		
		Iterator<String> realApNamesIterator = realApNamesMap.keySet().iterator();
		while(realApNamesIterator.hasNext()) {
			apFoldersList.add(realApNamesMap.get(realApNamesIterator.next()).getApFolder());
		}
		
		Iterator<String> bucketApNamesIterator = bucketApNamesMap.keySet().iterator();
		while(bucketApNamesIterator.hasNext()) {
			apFoldersList.add(bucketApNamesMap.get(bucketApNamesIterator.next()).getApFolder());
		}
		
		return apFoldersList;
	}
	
	/**
	 * 將 List 內的物件放到 Map 的 Key中，但不重覆放。
	 * @param apNamesList
	 * @param apNamesMap
	 */
	private void putApNameToMap(List<ApInfoBean> apInfoBeanList, Map<String ,ApInfoBean> apMap) {
		for (ApInfoBean apInfoBean : apInfoBeanList) {
			apMap.put(apInfoBean.getApName(), apInfoBean);
		}
	}
	
	@Override
	public synchronized void fillTheApFolderIntoBasicBusinessInfoBean(BasicBusinessInfoBean basicBusinessInfoBean) {
		List<ApInfoBean> apInfoBeanList = basicBusinessInfoBean.getSyushinBusinessApList();
		
		if (basicBusinessInfoBean.getBusinessType().equals(this.REAL_SYUSHIN)) {
			for (ApInfoBean apInfoBean : apInfoBeanList) {
				if (null == apInfoBean.getApFolder()) {
					apInfoBean.setApFolder(new File(SyushinBusinessApMgtActionImpl.syushinOutboxRealPathStr, apInfoBean.getApName()));
				}
			}
		}
		else if (basicBusinessInfoBean.getBusinessType().equals(this.BATCH_SYUSHIN)) {
			for (ApInfoBean apInfoBean : apInfoBeanList) {
				if (null == apInfoBean.getApFolder()) {
					apInfoBean.setApFolder(new File(SyushinBusinessApMgtActionImpl.syushinOutboxBucketPathStr, apInfoBean.getApName()));
				}
			}
		}
	}
	
	// --- getter and setter ---

	public void setSyushinBusinessApDao(ISyushinBusinessApDao syushinBusinessApDao) {
		this.syushinBusinessApDao = syushinBusinessApDao;
	}

	public void setBusinessManager(
			SyushinBusinessManager businessManager) {
		this.businessManager = businessManager;
	}

	public void setSyushinOutboxBucketPathStr(String syushinOutboxBucketPathStr) {
		SyushinBusinessApMgtActionImpl.syushinOutboxBucketPathStr = syushinOutboxBucketPathStr;
	}

	public void setSyushinOutboxRealPathStr(String syushinOutboxRealPathStr) {
		SyushinBusinessApMgtActionImpl.syushinOutboxRealPathStr = syushinOutboxRealPathStr;
	}

	public void setMessageUnitMonitor(UnitMonitor messageUnitMonitor) {
		this.messageUnitMonitor = messageUnitMonitor;
	}

	public void setREAL_SYUSHIN(String realSYUSHIN) {
		REAL_SYUSHIN = realSYUSHIN;
	}

	public void setBATCH_SYUSHIN(String batchSYUSHIN) {
		BATCH_SYUSHIN = batchSYUSHIN;
	}
}
